package jmine.tec.batch.event.domain;

import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

import jmine.tec.batch.BatchController;
import jmine.tec.batch.BatchException;
import jmine.tec.batch.JmineTecBatchMessages;
import jmine.tec.batch.context.BatchExecutionContext;
import jmine.tec.batch.event.domain.enumx.EventActionModeEnum;
import jmine.tec.batch.event.domain.enumx.EventLevelEnum;
import jmine.tec.batch.event.domain.enumx.EventStatusEnum;
import jmine.tec.persist.annotation.Alias;
import jmine.tec.persist.annotation.Comment;
import jmine.tec.persist.annotation.Constraint;
import jmine.tec.persist.annotation.DiscriminatorComment;
import jmine.tec.persist.annotation.Index;

import org.hibernate.Session;
import org.hibernate.annotations.Type;

import bancosys.tec.component.Documentation;
import bancosys.tec.persist.bussobj.PersistableBusinessObject;
import bancosys.tec.utils.date.Timestamp;

/**
 * Evento.
 * 
 * @author Frank Cara
 */
@Entity
@Table(name = "EVENT")
@Alias("EVENT")
@SequenceGenerator(name = "SEQ_EVENT", sequenceName = "SEQ_EVENT")
@DiscriminatorValue(value = "GERAL")
@DiscriminatorComment("GERAL")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Constraint(suffix = "3")
@DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING)
@Comment(table = "EVENT", column = "DISCRIMINATOR", value = "TIPO DO EVENTO.")
@Documentation("ARMAZENA O ESTADO DE EXECUCAO DOS EVENTOS DE UMA UNIDADE DE PROCESSAMENTO.")
public class Event extends PersistableBusinessObject {

    private Long id;

    private Timestamp timestamp;

    private EventActionModeEnum actionMode = EventActionModeEnum.DISPATCH;

    private EventStatusEnum status = EventStatusEnum.STARTED;

    private String mnemonico;

    private String descr;

    private String discriminator;

    private EventLevelEnum level;

    /**
     * Construtor padrão.
     */
    public Event() {
        super();
        this.timestamp = new Timestamp();
        this.level = EventLevelEnum.DEBUG;
    }

    /**
     * Construtor.
     * 
     * @param id - identificador do evento.
     */
    public Event(long id) {
        super();
        this.id = new Long(id);
    }

    /**
     * @return o id.
     */
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_EVENT")
    @Column(name = "COD_EVENT", nullable = false)
    @Documentation("CODIGO DO ESTADO DE EXECUCAO DE UM EVENTO.")
    public Long getId() {
        return this.id;
    }

    /**
     * Seta o id para a instancia.
     * 
     * @param id o id da instancia.
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * Retorna o timestamp.
     * 
     * @return o valor do timestamp.
     */
    @Index(suffix = "JT_0")
    @Type(type = "bancosys.tec.persist.hibernate.type.TimestampType")
    @Column(name = "TS_EVENT", nullable = false)
    @Documentation("DATA DE EXECUCAO DO EVENTO")
    public Timestamp getTimestamp() {
        return this.timestamp;
    }

    /**
     * Seta o timestamp.
     * 
     * @param timestamp - timestamp.
     */
    protected void setTimestamp(Timestamp timestamp) {
        this.timestamp = timestamp;
    }

    /**
     * Seta o timestamp.
     * 
     * @param ptimestamp - timestamp.
     */
    protected void setTimestamp(java.sql.Timestamp ptimestamp) {
        this.timestamp = new Timestamp(ptimestamp);
    }

    /**
     * @return se o evento é do modo SEND ou DISPATCH.
     */
    @Column(name = "ACTION_MODE", nullable = false)
    @Documentation("IDENTIFICA O MODO DE ACAO DE UM EVENTO QUE PODE SER DISPATCH (0) OU SEND (1).")
    @Constraint(suffix = "0")
    public EventActionModeEnum getActionMode() {
        return this.actionMode;
    }

    /**
     * Seta o modo do evento.
     * 
     * @param actionMode o modo do evento.
     */
    public void setActionMode(EventActionModeEnum actionMode) {
        this.actionMode = actionMode;
    }

    /**
     * @return o status do evento.
     */
    @Column(name = "STATUS", nullable = false)
    @Documentation("INDICA O ESTADO DA EXECUCAO DO EVENTO, QUE PODE SER: INICIADO (0), AGUARDANDO (1), FINALIZADO SEM ERROS (2).")
    @Constraint(suffix = "2")
    public EventStatusEnum getStatus() {
        return this.status;
    }

    /**
     * Seta o status do evento.
     * 
     * @param status o status do evento.
     */
    public void setStatus(EventStatusEnum status) {
        this.status = status;
    }

    /**
     * @return o discrimination do evento.
     */
    @Column(name = "DISCRIMINATOR", nullable = false, length = 20, insertable = false, updatable = false)
    public String getDiscriminator() {
        return this.discriminator;
    }

    /**
     * Seta o discrimination do evento.
     * 
     * @param discriminator o discrimination do evento.
     */
    public void setDiscriminator(String discriminator) {
        this.discriminator = discriminator;
    }

    /**
     * @return uma palavra que resuma o evento.
     */
    @Column(name = "MNE", length = 50)
    @Documentation("MNEMONICO DO EVENTO")
    public String getMnemonico() {
        return this.mnemonico;
    }

    /**
     * Seta o mnemônico.
     * 
     * @param mnemonico - mnemônico do evento.
     */
    public void setMnemonico(String mnemonico) {
        this.mnemonico = mnemonico;
    }

    /**
     * @return o texto do evento.
     */
    @Column(name = "DESCR", nullable = false, length = 1000)
    @Documentation("DESCRICAO DO EVENTO.")
    public String getDescr() {
        return this.descr;
    }

    /**
     * Seta a descrição.
     * 
     * @param descr - descrição do evento.
     */
    public void setDescr(String descr) {
        this.descr = descr;
    }

    /**
     * @return o texto do evento.
     */
    @Column(name = "LVL", nullable = false)
    @Documentation("INDICA O NIVEL DE EXECUCAO DO EVENTO, QUE PODE SER DEBUG (0), INFO (1), WARN (2), ERROR (3).")
    @Constraint(suffix = "1")
    public EventLevelEnum getLevel() {
        return this.level;
    }

    /**
     * Seta o nível do evento.
     * 
     * @param level - nível do evento.
     */
    public void setLevel(EventLevelEnum level) {
        this.level = level;
    }

    /**
     * Adquire o contexto de dados.
     * 
     * @param context - contexto de kernel.
     */
    public void acquireContextData(BatchExecutionContext context) {
        // Deve ser implementado nas subclasses para obter dados do contexto.
    }

    /**
     * Salva o evento utilizando o contexto transacional corrente.
     * 
     * @param controller o Controller do Kernel.
     */
    public void execute(BatchController controller) {
        if (EventStatusEnum.EXECUTED.equals(this.status)) {
            throw new BatchException(JmineTecBatchMessages.EVENT_EVENT_ALREADY_EXECUTED.create());
        }
        Session session = controller.getSessionFactory().getCurrentSession();
        if (session == null) {
            throw new BatchException(JmineTecBatchMessages.EVENT_NO_SESSION_BOUND_TO_THREAD_UNABLE_TO_SAVE_EVENT.create());
        }
        this.setStatus(EventStatusEnum.EXECUTED);
        session.save(this);
    }
}
